//refer to ColorRectDetector.java and CVMotionDetector
//this version is to test thresholding and find Blob  (21 Aug 2012)
package test;

import java.awt.Dimension;
import java.awt.Image;
import java.awt.Point;

import org.omg.CORBA.portable.CustomValue;

import static com.googlecode.javacv.cpp.opencv_core.*;
import static com.googlecode.javacv.cpp.opencv_highgui.*;
import static com.googlecode.javacv.cpp.opencv_imgproc.*;

import com.googlecode.javacpp.Loader;
import com.googlecode.javacpp.Pointer;
import com.googlecode.javacv.*;
import com.googlecode.javacv.cpp.*;
import com.googlecode.javacv.cpp.opencv_core.CvScalar;

public class HandDetector {

	// HSV value on skin extraction
	public static final int SKIN_HUE_MAX = 0;
	public static final int SKIN_HUE_MIN = 20;

	public static final int SKIN_SAT_MAX = 30;
	public static final int SKIN_SAT_MIN = 150;

	public static final int SKIN_VOL_MAX = 80;
	public static final int SKIN_VOL_MIN = 255;

	public static final int SMALLEST_AREA = 0;

	public static final int MIN_PIXELS = 100;

	// Memory
	private CvMemStorage storage;

	// HSV Arrays
	private CvScalar hsv_min, hsv_max;

	private IplImage srcImg, hsv_image, hsv_mask;// source image ,Image
													// manipulated
	private Dimension imDim; // resolution of Image

	// Constructors
	public HandDetector(int width, int height) {
		// Fix Resolution
		// Now i've got bug
		hsv_min = new CvScalar(132, 143, 0, 0);
		hsv_max = new CvScalar(179, 255, 255, 0);

		hsv_image = IplImage.create(width, height, 8, 3);
		hsv_mask = IplImage.create(width, height, 8, 1);
		imDim = new Dimension(width, height);

		storage = CvMemStorage.create();

	}

	public HandDetector(IplImage firstFrame) {
		// Rely on Frame Capture Resolution

		hsv_min = new CvScalar(0, 30, 80, 0);
		hsv_max = new CvScalar(20, 150, 255, 0);

		hsv_image = IplImage.create(firstFrame.width(), firstFrame.height(), 8,
				3);
		hsv_mask = IplImage.create(firstFrame.width(), firstFrame.height(), 8,
				1);
		imDim = new Dimension(firstFrame.width(), firstFrame.height());

		storage = CvMemStorage.create();
	}

	// Relate Functions

	public IplImage convertToThresholdFrame(IplImage srcImage) {
		// convert Iplimage frame from BGR to HSV then find threshold in Range
		// return as hsv_mask
		cvCvtColor(srcImage, hsv_image, CV_BGR2HSV);
		cvInRangeS(hsv_image, hsv_min, hsv_max, hsv_mask);
		cvSmooth(hsv_mask, hsv_mask, CV_GAUSSIAN, 1);

		return hsv_mask;

	}

	public CvSeq findHandContour(IplImage srcImage) {
		// implemented from getFingertips Pseudo code.
		// return as cvSeq 20.08.2012

		CvSeq bigger_contours = null;
		CvRect bigRect = null;

		CvSeq contours = new CvSeq(null);
		cvFindContours(srcImage, storage, contours,
				Loader.sizeof(CvContour.class), CV_RETR_EXTERNAL,
				CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));

		// Improve contouring simpler.
		if (contours != null)
			contours = cvApproxPoly(contours, Loader.sizeof(CvContour.class),
					storage, CV_POLY_APPROX_DP, 25, 1);

		// find the bigger contour

		int area = SMALLEST_AREA;

		while (contours != null && !contours.isNull()) {
			CvRect rect = cvBoundingRect(contours, 0);
			if ((rect.width() * rect.height()) > area) {
				bigger_contours = contours;
				area = rect.width() * rect.height();
				bigRect = rect;
			}

			contours = contours.h_next();

		}

		cvClearMemStorage(storage);
		// cvReleaseMemStorage(storage);
		return bigger_contours;

	}

	public CvSeq findConvexHull(CvSeq biggest_contours) {

		CvRect rect = cvBoundingRect(biggest_contours, 1);
		// System.out.println(rect);

		int numPoints = biggest_contours.total();
		System.out.println("CT:" + numPoints);
		CvPoint contourPoints = new CvPoint(numPoints);
		cvCvtSeqToArray(biggest_contours, contourPoints.position(0),
				CV_WHOLE_SEQ);

		// find ConvexHull for contour

		CvSeq seqHull = cvConvexHull2(biggest_contours, storage, CV_CLOCKWISE,
				0);
		System.out.println("CV" + seqHull.total());
		CvPoint hullPoints = new CvPoint(seqHull.total());
		cvCvtSeqToArray(seqHull, hullPoints.position(0), CV_WHOLE_SEQ);

		return seqHull;
	}

	public Point findCOG(IplImage srcImage) {
		Point pt = null;
		int numPixels = cvCountNonZero(srcImage);
		if (numPixels > MIN_PIXELS) {

		}
		return pt;
	}

	private void checkDrawContour(IplImage bwImage, IplImage dstImage) {
		int total = 0;
		CvSeq contour2 = new CvSeq(null);
		CvMemStorage storage2 = cvCreateMemStorage(0);
		CvMemStorage storageHull = cvCreateMemStorage(0);
		total = cvFindContours(bwImage, storage2, contour2,
				Loader.sizeof(CvContour.class), CV_RETR_LIST,
				CV_CHAIN_APPROX_SIMPLE);
		if (total > 1) {
			while (contour2 != null && !contour2.isNull()) {
				if (contour2.elem_size() > 0) {
					CvSeq points = cvApproxPoly(contour2,
							Loader.sizeof(CvContour.class), storage2,
							CV_POLY_APPROX_DP,
							cvContourPerimeter(contour2) * 0.005, 0);
					cvDrawContours(dstImage, points, CvScalar.BLUE,
							CvScalar.BLUE, -1, 1, CV_AA);
				}
				contour2 = contour2.h_next();
				// CvSeq.PointerByReference contours2 = new
				// CvSeq.PointerByReference(); <- does not work/no such function
				// CvSeq hull = cvConvexHull2(contour2, null, CV_CLOCKWISE, 0);
				// CvSeq defect = cvConvexityDefects(contour2, hull,
				// storageHull);
			}

		}

	}

	// Getter
	public IplImage getSrcImg() {
		return srcImg;
	}

	public IplImage getThresholdImg() {
		return hsv_mask;
	}

	public Dimension getImgDim() {
		return imDim;
	}

	// Setter
	public void setHSVMin(int h, int s, int v) {
		setHSVMin(h, s, v);
	}

	public void setHSVMax(int h, int s, int v) {
		setHSVMax(h, s, v);
	}

	public void setImgDim(int w, int h) {
		imDim = new Dimension(w, h);
	}

	public void setSrcImg(IplImage srcImage) {
		srcImg = srcImage;
	}

	// Test Part

	public static void main(String[] args) throws Exception {

		System.out.println("Initialize thresholder");

		// Src Canvas and converted Canvas
		CanvasFrame srcCanvas = new CanvasFrame("Capture Screen");
		CanvasFrame convrtCanvas = new CanvasFrame("Converted Screen");
		CanvasFrame convexCanvas = new CanvasFrame("Convex Hull");
		System.out.println("Canvas : done");

		OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(CV_CAP_ANY);
		grabber.start();

		// Start Hand Detect

		HandDetector hd = new HandDetector(640, 480);

		// get size of image
		Dimension imgDim = hd.getImgDim();
		IplImage img = IplImage.create(imgDim.width, imgDim.height,
				IPL_DEPTH_8U, 3);
		img = grabber.grab();
		System.out.println(img.width() + "*" + img.height());

		while (srcCanvas.isVisible() && convrtCanvas.isVisible()) {

			img = grabber.grab();
			IplImage gr_img = hd.convertToThresholdFrame(img);
			CvSeq points = hd.findHandContour(gr_img);
			CvSeq hull = hd.findConvexHull(points);

			cvDrawContours(gr_img, hull, CvScalar.BLUE, CvScalar.BLUE, -1, 1,
					CV_AA);
			cvDrawContours(img, points, CvScalar.BLUE, CvScalar.BLUE, -1, 1,
					CV_AA);
			// cvDrawPolyLine(img, hull, npts , contours, is_closed, color,
			// thickness, line_type, shift)

			srcCanvas.showImage(img);
			convrtCanvas.showImage(gr_img);
			convexCanvas.showImage(img);

		}
		grabber.stop();
		srcCanvas.dispose();
		convrtCanvas.dispose();

	}

}
